<attachment contenteditable="false" data-atts="%5B%5D" data-aid=".atts-d5b52b6f-9ffd-40e4-9b09-7bd9776cb217"></attachment><h1><a href="https://www.cnblogs.com/Actexpler-S/p/8026962.html" target="_blank" style="color: rgb(7, 93, 179);">Spring&nbsp;</a><a href="https://www.cnblogs.com/Actexpler-S/p/8026962.html" target="_blank" style="color: black; background-color: rgb(255, 255, 102);"><strong>Ldap</strong></a><a href="https://www.cnblogs.com/Actexpler-S/p/8026962.html" target="_blank" style="color: rgb(7, 93, 179);">&nbsp;user登录 userPassword验证</a></h1><p><strong style="background-color: rgb(255, 255, 102);">Ldap</strong>&nbsp;密码字段被加密,在用户登录 或者 用户更改密码时候都需要密码验证:</p><h2>1.自己的实现</h2><pre class="ql-syntax" spellcheck="false">/**
 * 集成Ldap登录验证
 *
 * @author wzc
 * @date 2017年12月11日 下午2:14:37
 *
 */
@Service
public class LdapLogin {
    /**
     * 创建一个LdapTemplate对象
     * 连接ldap
     */
    @Autowired
    private LdapTemplate ldapTemplate;
    
     private ContextSource contextSource;  
     

    /**
     * 登录验证Ldap
     * @param cn   ,登录的用户名
     * @param pwd  密码
     * @return
     */
    public  boolean  loginLdap(String cn , String pwd){
        //根据cn ,构建DN
        String dn = getDnForUser(cn);
        //密码检验
        boolean result = authenticate(dn,pwd);
        return  result;
        
    }
    
     /**  
      * 根据用户名密码验证  
      * @param userCn   用户名 
      * @param pwd  密码  
      * @return  
      */    
        @SuppressWarnings("unchecked")
        public boolean authenticate(String userCn, String pwd) {   
             DirContext ctx = null;    
             System.out.println(userCn +":"+pwd);
             try {    
                 //调用ldap 的 authenticate方法检验
                 boolean authenticate = ldapTemplate.authenticate(userCn, "(objectclass=person)", pwd);
                 return authenticate;    
             } catch (Exception e) {    
                  e.printStackTrace();
                 return false;    
             } finally {     
                 LdapUtils.closeContext(ctx);    
             }    
      
         } 
         /**
          * 根据cn 构建出 entry 的 Dn
          * @param cn
          * @return
          */
         @SuppressWarnings({ "unused", "unchecked" })
         private String getDnForUser(String cn) {
             
            List&lt;String&gt; results = ldapTemplate.search("", "(&amp;(objectclass=person)(cn="+cn+"))",  new DnMapper());
             
             if (results.size() != 1) {    
                 throw new RuntimeException("User not found or not unique");    
             }    
             System.out.println(results.get(0));
             return results.get(0);    
          }

   }

     /**
      * 
      * 节点的 Dn映射
      *
      * @author wzc
      * @date 2017年12月12日 上午11:21:09
      *
      */
     class DnMapper implements ContextMapper{
        @Override
        public String mapFromContext(Object ctx) {
             DirContextAdapter context = (DirContextAdapter)ctx;
             Name name = context.getDn();
             String dn = name.toString();
            return dn;
        }
    }
</pre><h2>参考&nbsp;<a href="http://angelbill3.iteye.com/blog/2321533" target="_blank" style="color: rgb(255, 0, 0);">http://angelbill3.iteye.com/blog/2321533</a></h2><p>如果要总结Spring的<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>（Spring开发的操作<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>的开源Jar），必须要从<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>说起。</p><p><strong style="background-color: rgb(255, 255, 102);">LDAP</strong>：Lightweight Directory Access Protocol，翻译过来是轻量级目录访问协议。</p><p>它是基于X.500标准的（X.500：构成全球分布式名录系统的协议），说的这么抽象基本上理解不了，只需要知道是一种协议，以目录的形式（结构树）来管理资原（用户、用户组、地址簿、邮件用户等）。一些大公司会选择以<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>来存储用户及其信息。</p><p>所以就像是数据库一般，<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>也是有client端和server端。server端是用来存放资源，client端用来操作增删改查等操作。</p><h3>1.&nbsp;<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>：Schema</h3><p><br></p><p>在<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>中目录是按照树型结构组织——目录信息树（DIT）</p><p>directory information tree (DIT).</p><p>DIT由（Entry）组成，条目相当于关系数据库中表的记录；</p><p>条目是具有分辨名DN（Distinguished&nbsp;Name）的属性-值对（Attribute-value）的集合。（DN相当于关系型数据库表中的主键Primary key）</p><p>关于<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>的基础要学习的还有很多，比如客户端的安装、数据模型的学习等等。</p><h3>2. Spring&nbsp;<strong style="background-color: rgb(255, 255, 102);">LDAP</strong></h3><p><br></p><p>Spring&nbsp;<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>就是基于JAVA开发的<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>客户端开源工具，主要用来操用<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>，其实现方法有点类似Spring JdbcTemplate（这个大家都非常熟悉了~）</p><p>支持Transaction （事务）</p><p>支持Pooling (连接池)</p><p>官网：<a href="http://www.springframework.org/ldap" target="_blank" style="color: rgb(7, 93, 179);">http://www.springframework.org/</a><a href="http://www.springframework.org/ldap" target="_blank" style="color: black; background-color: rgb(255, 255, 102);"><strong>ldap</strong></a></p><p>官方文档及例子（重要）：<a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/" target="_blank" style="color: rgb(7, 93, 179);">http://docs.spring.io/spring-</a><a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/" target="_blank" style="color: black; background-color: rgb(255, 255, 102);"><strong>ldap</strong></a><a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/" target="_blank" style="color: rgb(7, 93, 179);">/docs/2.1.0.RELEASE/reference/</a></p><p>JAVA文档（重要）：<a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/" target="_blank" style="color: rgb(7, 93, 179);">http://docs.spring.io/spring-</a><a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/" target="_blank" style="color: black; background-color: rgb(255, 255, 102);"><strong>ldap</strong></a><a href="http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/" target="_blank" style="color: rgb(7, 93, 179);">/docs/2.1.0.RELEASE/apidocs/</a></p><p>GitHub（大量例子）：<a href="https://github.com/spring-projects/spring-ldap" target="_blank" style="color: rgb(7, 93, 179);">https://github.com/spring-projects/spring-</a><a href="https://github.com/spring-projects/spring-ldap" target="_blank" style="color: black; background-color: rgb(255, 255, 102);"><strong>ldap</strong></a></p><h3>3. 核心类：LdapTemplate</h3><p><br></p><p>这个类非常类似Spring JdbcTemplate，JdbcTemplate的实现是通过传入sql语句和RowMapper，query返回目标列表，或是传入sql和参数，执行update方法。JdbcTemplate的优点是简化了与数据库连接的代码（实现了<strong style="background-color: rgb(255, 255, 102);">ldap</strong>属性到对象的映射，使得代码更为简单和优雅），以及避免了一些常见的错误。（这个开源已经更新到4+版本了，可见其应用之广）。</p><p>优点都是相通的，Spring LdapTemplate的优点是简化了与<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>交互的代码（传统的类详见：</p><p><a href="http://docs.oracle.com/javase/7/docs/api/javax/naming/directory/package-summary.html" target="_blank" style="color: rgb(7, 93, 179);">http://docs.oracle.com/javase/7/docs/api/javax/naming/directory/package-summary.html</a>）以及避免了一些常见的错误。</p><h3>4. 怎样理解Autherntication</h3><p><br></p><p>要验证一个<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>的Entry的身份（有点类似于用户名、密码<strong style="background-color: rgb(153, 255, 153);">登陆</strong>），<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>的思路是通过DN搜索到目标Entry（例如一个公司员工），那么再通过这个Entry和password来验证合法性。</p><p>具体的业务比如是：一个员工要<strong style="background-color: rgb(153, 255, 153);">登陆</strong>公司网站，输入他的员工号和密码。我们是不能通过查询在<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>里拿到用户的密码（安全性的限制），那么只能传入实际的密码，让<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>&nbsp;server端验证合法性。</p><p>ldapTemplate.authenticate(LdapQuery query, String password);</p><p>在使用这个方法的时候曾经遇到过一个问题，如下：</p><p>调用ldapTemplate.authenticate时验证老是通不过（always return false），经查文档发现：如果<strong style="background-color: rgb(255, 255, 102);">ldap</strong>连接是有连接池的话，那么总是调用已创建好的连接去验证，这样是错误的。Authenticate的验证过程需要ContextSource通过传入的待验证的用户名和密码来重新绑定生成一个连接，也就是说这个方法要使用到的connection连接并不能是连接池里的那个connection。</p><p>所以需要new一个LdapContextSource类和LdapTemplate类，再通过LdapTemplate类的setContextSource(ContextSource contextSource)将持有用户名密码的ContextSource传入。</p><p>注意：在contextSource创建后，需要调用afterPropertiesSet()方法来验证所有必要的参数都已经set了（特指url、用户名、密码等），这个方法执行后，真正的contextSource才会被实例化。（特别是在Spring context上下文之外的配置中，必须要执行该方法。</p><p>这么说好像很抽象，具体代码如下：</p><pre class="ql-syntax" spellcheck="false">1.LdapContextSource contextSource = new LdapContextSource();  
2.contextSource.setUrl(url);  
3.contextSource.setUserDn(userDn);  
4.contextSource.setPassword(userPwd);  
5.contextSource.setPooled(false);  
6.contextSource.afterPropertiesSet(); // important
7.
8.LdapTemplate template = new LdapTemplate();  
9.template.setContextSource(contextSource);  
10.
11.Boolean result = template.authenticate(LDAP_BASE_DN, filter, pwd);
</pre><p><strong>5. Pooling</strong></p><p>Spring&nbsp;<strong style="background-color: rgb(255, 255, 102);">LDAP</strong>的pool用的是apache commons pool（<a href="http://commons.apache.org/proper/commons-pool/index.html" target="_blank" style="color: rgb(7, 93, 179);">http://commons.apache.org/proper/commons-pool/index.html</a>）</p><p><strong>6. 通过SSL的认证方式连接</strong></p><p>这块公司是用IBM的portal来安装的SSL，所以对于tomcat的配置并不是很了解。可以在stack-overflow上看看相关资料。</p><p><strong>【总结】</strong></p><p>在使用Spring&nbsp;<strong style="background-color: rgb(255, 255, 102);">ldap</strong>的一年多时间里，没有碰到太过复杂的问题，产品上线后表现也很稳定。总得来说因为跟大家都熟悉的JdbcTemplate思想上有些相似，所以学习起来成本也不高。</p><p><br></p>